use crate::error::{Error, Result};
use serde_derive::{Deserialize, Serialize};
use std::convert::Infallible;
use tokio::sync::{mpsc, oneshot};
use tracing::{debug, error, info};
use warp::{reject, reply::json, Filter, Reply};

/// id, 和 js 端定义相同
#[derive(Serialize, Deserialize)]
pub struct Id {
    value: usize,
}

impl Id {
    pub fn new(value: usize) -> Self {
        Id { value }
    }
}

#[derive(Debug)]
enum Cmd {
    Get(oneshot::Sender<usize>),
    Drop(usize),
}

/// 包装下 sender
#[derive(Clone)]
pub struct IdManager {
    sender: mpsc::Sender<Cmd>,
}

impl IdManager {
    pub async fn new() -> Self {
        let (s, r) = mpsc::channel(32);
        tokio::spawn(async move { id_manager_task(r).await });
        debug!("new id manager");
        IdManager { sender: s }
    }
}

/// 获得一个新的 id
pub async fn get_id(id_manager: IdManager) -> Result<impl Reply> {
    let (s, r) = oneshot::channel();
    id_manager.sender.send(Cmd::Get(s)).await.unwrap();

    match r.await {
        Ok(id) => Ok(json(&Id::new(id))),
        Err(_) => Err(reject::custom(Error::ChannelError)),
    }
}

/// id manager filter
pub fn with_id_manager(
    id_manager: IdManager,
) -> impl Filter<Extract = (IdManager,), Error = Infallible> + Clone {
    warp::any().map(move || id_manager.clone())
}

async fn id_manager_task(mut rc: mpsc::Receiver<Cmd>) {
    info!("init manager task");
    let mut cur_id = 0;
    loop {
        if let Some(cmd) = rc.recv().await {
            use Cmd::*;
            match cmd {
                Get(sender) => {
                    sender.send(cur_id).unwrap();
                    cur_id += 1;
                }
                Drop(_id) => {}
            }
        }
        debug!("one recv");
    }
}
